package com.wanban.download.downloadutils

import android.net.Uri
import android.util.Log
import androidx.annotation.OptIn
import androidx.media3.common.C
import androidx.media3.common.C.TrackType
import androidx.media3.common.DrmInitData
import androidx.media3.common.Format
import androidx.media3.common.MediaItem
import androidx.media3.common.Tracks
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.DefaultRenderersFactory
import androidx.media3.exoplayer.drm.DrmSession
import androidx.media3.exoplayer.offline.Download
import androidx.media3.exoplayer.offline.DownloadHelper
import androidx.media3.exoplayer.offline.DownloadHelper.LiveContentUnsupportedException
import androidx.media3.exoplayer.offline.DownloadRequest
import androidx.media3.exoplayer.offline.DownloadService
import com.google.common.collect.ImmutableList
import com.wanban.download.BuildConfig
import com.wanban.download.ContextProvider
import com.wanban.download.WBDownloadUtils
import com.wanban.download.base.BaseDownloadUtils
import com.wanban.download.db.DownloadEntity
import com.wanban.download.exo.DemoUtil
import com.wanban.download.exo.WidevineOfflineLicenseFetchTask
import com.wanban.download.listener.OnFileDownloadListener
import com.wanban.download.listener.OnM3U8ParseListener
import com.wanban.download.model.M3U8
import com.wanban.download.service.Media3DownloadService
import com.wanban.download.utils.CacheM3U8Utils
import com.wanban.download.utils.DownloadConstants
import com.wanban.download.utils.M3U8ParseUtils
import com.wanban.download.utils.PathUtils
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit


@OptIn(UnstableApi::class)
class Media3DownloadUtils: BaseDownloadUtils {

    private val TAG = "Media3Download"
    private val errorReason = 99
    private var task: DownloadEntity? = null
    private var listener: OnFileDownloadListener? = null
    private val downloadManager = DemoUtil.getDownloadManager(ContextProvider.application)
    private var widevineOfflineLicenseFetchTask: WidevineOfflineLicenseFetchTask? = null
    private var uiDisposable: Disposable? = null
    private var speed = 0L
    private var interval = false
    private var parseUtils: M3U8ParseUtils? = null


    companion object {
        fun deleteTask(id: String){
            DownloadService.sendRemoveDownload(
                ContextProvider.application,
                Media3DownloadService::class.java,
                id,
                /* foreground= */ false
            )
        }
    }

    override fun startDownload(task: DownloadEntity, listener: OnFileDownloadListener) {
        this.task = task
        this.listener = listener
        try {
            parseUtils?.cancel()
            if (task.m3u8?.tsList?.isNotEmpty() == true) { // 如果已经存在的，不会再次解析
                // 准备下载
                if (interval) return
                execDownload(task, listener)
                return
            }
            parseUtils = M3U8ParseUtils()
            parseUtils?.parseUrl(task.url, task.fileRootPath, task.downloadTag, object: OnM3U8ParseListener {
                override fun onParseSuccess(m3u8: M3U8) {
                    Log.e("ExchangeManager", "readyToDownload -> ${task.downloadTag} -> 解析成功....")
                    Log.d(WBDownloadUtils.TAG, "${task.downloadTag} -> onParseSuccess...是否是MP4:" +m3u8.isMp4)
                    if (parseUtils?.isCanceled() == true)  { // 可能因为暂停或者删除等原因，取消了下载
                        return
                    }
                    parseUtils = null
                    CacheM3U8Utils.cacheM3U8(task.downloadTag, m3u8)
                    if (interval) return
                    // 准备下载
                    execDownload(task, listener)
                }

                override fun onParseFail(errMsg: String) {
                    Log.e(WBDownloadUtils.TAG, "${task.downloadTag} -> onParseFail...$errMsg")
                    if (parseUtils?.isCanceled() == true) { // 可能因为暂停或者删除等原因，取消了下载
                        return
                    }
                    parseUtils = null
                    if (interval) return
                    execDownload(task, listener)
                }
            }, task.header)


//            execDownload(task, listener)
        } catch (e: Exception) {
            e.printStackTrace()
            stopUIDisposable()
            listener.onError(task.url, DownloadConstants.ERROR_DOWNLOAD_UNKNOWN)
        }

    }

    private fun execDownload(task: DownloadEntity, listener: OnFileDownloadListener) {
        val downloadRequest = DownloadRequest.Builder(task.downloadTag, Uri.parse(task.url)).build()
        DownloadService.sendAddDownload(
            ContextProvider.application,
            Media3DownloadService::class.java,
            downloadRequest,
            /* foreground= */ false
        )
        listener.onStart(task.url)
        startUIDisposable()
    }




    private fun startUIDisposable(){
        uiDisposable?.dispose()
        uiDisposable = Flowable.interval(1000, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({
                if (uiDisposable?.isDisposed == true) {
                    return@subscribe
                }
                if (task == null) {
                    uiDisposable?.dispose()
                    return@subscribe
                }
                var download = downloadManager.currentDownloads.firstOrNull { it.request.id == task?.downloadTag }
                if (download == null) {
                    download = downloadManager.downloadIndex.getDownload(task!!.downloadTag)?:return@subscribe
                }
                val url = download.request.uri.toString()
                when (download.state){
                    Download.STATE_FAILED -> {
                        Log.d(TAG, "STATE_FAILED -> 。。。")
                        stopUIDisposable()
                        listener?.onError(url, DownloadConstants.ERROR_DOWNLOAD_UNKNOWN)
                    }
                    Download.STATE_COMPLETED -> {
                        Log.d(TAG, "STATE_COMPLETED -> 。。。")
                        stopUIDisposable()
                        listener?.onComplete(url, download.bytesDownloaded, url)
                    }
                    Download.STATE_STOPPED -> {
                        Log.d(TAG, "state -> STATE_STOPPED")
                        stopUIDisposable()
                    }
                    Download.STATE_DOWNLOADING -> {
                        var contentLength = download.bytesDownloaded
                        if (contentLength < 0) contentLength = 0

                        var currentSpeed = 0L
                        if (speed > 0) {
                            currentSpeed = download.bytesDownloaded - speed
                        }
                        speed = download.bytesDownloaded
                        if (currentSpeed < 0) currentSpeed = 0
                        if (BuildConfig.DEBUG) {
                            val speedStr = DownloadConstants.byte2FitMemorySize(currentSpeed, 2)
                            val currentLengthStr = DownloadConstants.byte2FitMemorySize(contentLength, 2)
                            Log.e(TAG, "STATE_DOWNLOADING -> $speedStr/s - $currentLengthStr, ${download.percentDownloaded}% ")
                        }
                        if (uiDisposable?.isDisposed == true) {
                            return@subscribe
                        }
                        listener?.onProgress(download.request.uri.toString(), currentSpeed, contentLength, download.percentDownloaded)
                    }
                    Download.STATE_REMOVING -> {
                        stopUIDisposable()
                        Log.d(TAG, "state -> STATE_REMOVING")
                    }
                    Download.STATE_RESTARTING -> {
                        Log.d(TAG, "state -> STATE_RESTARTING")
                        startUIDisposable()
                    }
                    else -> {

                    }
                }
            }, {
                it?.printStackTrace()
            })
    }

    private fun stopUIDisposable(){
        interval = true
        uiDisposable?.dispose()
        uiDisposable = null
    }

    override fun stopDownload() {
        try {
            parseUtils?.cancel()
            stopUIDisposable()
            downloadManager.setStopReason(task?.downloadTag, errorReason)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun deleteExoTask(id: String){
        // 再删除下载任务
        downloadManager.removeDownload(id)

//        DownloadService.sendRemoveDownload(
//            ContextProvider.application,
//            Media3DownloadService::class.java,
//            id,
//            /* foreground= */ false
//        )
    }

    override fun deleteDownloadFile(task: DownloadEntity) {
        // 再删除下载任务
        deleteExoTask(task.downloadTag)
        PathUtils.deleteTaskFiles(task)

//        Flowable.timer(10, TimeUnit.SECONDS)
//            .subscribeOn(Schedulers.io())
//            .subscribe({
//                val context = ContextProvider.application
//
//                deleteExoTask(task.downloadTag)
//
////                val m3u8 = CacheM3U8Utils.getCacheM3U8(task.downloadTag)
////                if (m3u8?.tsList.isNullOrEmpty()) return@subscribe
////                val cache = DemoUtil.getDownloadCache(context)?:return@subscribe
////
////                for (ts in m3u8?.tsList!!) {
//////                    Log.e("DELETE_EXO", "ts -> ${ts.url}")
////                    if (TextUtils.isEmpty(ts.url)) continue
////                    val cacheResult = cache.getCachedSpans(ts.url!!)
////                    if (cacheResult.isEmpty()) {
////                        Log.d("DELETE_EXO", "指定缓存已经被删除！")
////                        continue
////                    }
////                    Log.d("DELETE_EXO", "找到指定缓存文件！")
////                    cacheResult.forEach { cacheSpan ->
////                        cache.releaseHoleSpan(cacheSpan)
////                        if (cacheSpan?.file?.exists() == true) {
////                            Log.d("DELETE_EXO", "${cacheSpan.file?.nameWithoutExtension} -> 文件存在！")
////                            val isDeleteSuccess = cacheSpan.file?.deleteRecursively()?:false
////                            if (!isDeleteSuccess) {
////                                Log.e("DELETE_EXO", "${cacheSpan.file?.nameWithoutExtension} -> 文件删除失败！")
//////                                    Toast.makeText(context, "${cacheSpan.file?.nameWithoutExtension} -> 文件删除失败", Toast.LENGTH_SHORT).show()
////                            } else {
////                                Log.d("DELETE_EXO", "${cacheSpan.file?.nameWithoutExtension} -> ts文件删除成功！")
////                            }
////                        } else {
////                            Log.d("DELETE_EXO", "${cacheSpan.file?.nameWithoutExtension} -> ts文件已成功删除！")
////                        }
////                    }
////                }
//                PathUtils.deleteTaskFiles(task)
//            }, {
//                it?.printStackTrace()
//                PathUtils.deleteTaskFiles(task)
//            }).isDisposed
    }

    private val SUPPORTED_TRACK_TYPES: ImmutableList<Int> = ImmutableList.of(
        C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_TEXT, C.TRACK_TYPE_IMAGE
    )
    private fun willHaveContent(tracks: Tracks): Boolean {
        for (trackGroup in tracks.groups) {
            if (SUPPORTED_TRACK_TYPES.contains(trackGroup.type)) {
                return true
            }
        }
        return false
    }


    /**
     * Returns the first [Format] with a non-null [Format.drmInitData] found in the
     * content's tracks, or null if none is found.
     */
    private fun getFirstFormatWithDrmInitData(helper: DownloadHelper): Format? {
        for (periodIndex in 0 until helper.periodCount) {
            val mappedTrackInfo = helper.getMappedTrackInfo(periodIndex)
            for (rendererIndex in 0 until mappedTrackInfo.rendererCount) {
                val trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex)
                for (trackGroupIndex in 0 until trackGroups.length) {
                    val trackGroup = trackGroups[trackGroupIndex]
                    for (formatIndex in 0 until trackGroup.length) {
                        val format: Format = trackGroup.getFormat(formatIndex)
                        if (format.drmInitData != null) {
                            return format
                        }
                    }
                }
            }
        }
        return null
    }


    /**
     * Returns whether any [DrmInitData.SchemeData] that [ ][DrmInitData.SchemeData.matches] [C.WIDEVINE_UUID] has non-null [ ][DrmInitData.SchemeData.data].
     */
    private fun hasNonNullWidevineSchemaData(drmInitData: DrmInitData?): Boolean {
        if (drmInitData == null) return false
        for (i in 0 until drmInitData.schemeDataCount) {
            val schemeData: DrmInitData.SchemeData = drmInitData.get(i)
            if (schemeData.matches(C.WIDEVINE_UUID) && schemeData.hasData()) {
                return true
            }
        }
        return false
    }
    private fun execDownloadPrepare(task: DownloadEntity, listener: OnFileDownloadListener){
        val downloadHelper =
            DownloadHelper.forMediaItem(
                ContextProvider.application,
                MediaItem.fromUri(task.url),
                DefaultRenderersFactory(ContextProvider.application),
                DemoUtil.getHttpDataSourceFactory()
            )
        downloadHelper.prepare(object: DownloadHelper.Callback{
            override fun onPrepared(helper: DownloadHelper) {
                val format = getFirstFormatWithDrmInitData(helper)
                if (format == null) {
                    onDownloadPrepared(helper, task, listener)
                    return
                }
                // The content is DRM protected. We need to acquire an offline license.
                if (!hasNonNullWidevineSchemaData(format.drmInitData)) {
                    // Failed to obtain offline license
                    Log.e(TAG, "不支持下载DRM方案数据不在清单中的内容")
                    return
                }
                val mediaItem = MediaItem.fromUri(task.url)
                widevineOfflineLicenseFetchTask = WidevineOfflineLicenseFetchTask(
                    format,
                    mediaItem.localConfiguration?.drmConfiguration,
                    DemoUtil.getHttpDataSourceFactory(),
                    downloadHelper,
                    object: WidevineOfflineLicenseFetchTask.OnWidevineOfflineLicenseListener {
                        override fun onOfflineLicenseFetchedError(drmSessionException: DrmSession.DrmSessionException?) {
                            listener.onError(task.url, "无法获取离线DRM许可证")
                        }

                        override fun onOfflineLicenseFetched(downloadHelper: DownloadHelper, keySetId: ByteArray?) {
                            onDownloadPrepared(downloadHelper, task, listener)
                        }
                    })
                widevineOfflineLicenseFetchTask?.execute()

            }

            override fun onPrepareError(helper: DownloadHelper, e: IOException) {
                val isLiveContent = e is LiveContentUnsupportedException
                val toastString: String = if (isLiveContent) "不支持下载直播内容！" else "无法正常下载"
                listener.onError(task.url, toastString)
            }
        })

    }

    private fun onDownloadPrepared(helper: DownloadHelper, task: DownloadEntity, listener: OnFileDownloadListener) {
        if (helper.periodCount == 0) {
            Log.d(TAG, "No periods found. Downloading entire stream.")
            execDownload(task, listener)
            helper.release()
            return
        }

        val tracks: Tracks = helper.getTracks( /* periodIndex= */0)
        if (!willHaveContent(tracks)) {
            Log.d(TAG, "No dialog content. Downloading entire stream.")
            execDownload(task, listener)
            helper.release()
            return
        }


//        trackSelectionDialog =
//            TrackSelectionDialog.createForTracksAndParameters( /* titleId= */
//                R.string.exo_download_description,
//                tracks,
//                DownloadHelper.getDefaultTrackSelectorParameters(context),  /* allowAdaptiveSelections= */
//                false,  /* allowMultipleOverrides= */
//                true,  /* onTracksSelectedListener= */
//                this,  /* onDismissListener= */
//                this
//            )
//        trackSelectionDialog.show(fragmentManager,  /* tag= */null)

        for (i in SUPPORTED_TRACK_TYPES.indices) {
            val trackType: @TrackType Int = SUPPORTED_TRACK_TYPES[i]
            val trackGroups = ArrayList<Tracks.Group>()
            for (trackGroup in tracks.groups) {
                if (trackGroup.type == trackType) {
                    trackGroups.add(trackGroup)
                }
            }
//            if (trackGroups.isNotEmpty()) {
//                val tabFragment: TrackSelectionViewFragment = TrackSelectionViewFragment()
//                tabFragment.init(
//                    trackGroups,
//                    trackSelectionParameters.disabledTrackTypes.contains(trackType),
//                    trackSelectionParameters.overrides,
//                    allowAdaptiveSelections,
//                    allowMultipleOverrides
//                )
//                tabFragments.put(trackType, tabFragment)
//                tabTrackTypes.add(trackType)
//            }
        }
    }


}